package com.carbohydrate42.kotlin_compose_demo.compose

import android.Manifest
import android.annotation.SuppressLint
import android.graphics.Typeface
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.animation.core.TweenSpec
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import java.util.concurrent.Executors

val cameraMainColor = Color.Gray

/**
 * 二维码扫描, 用 MLkit 和 cameraX 扫描条形码和二维码 (ZXING 扫描的效果不大好)
 * https://www.jianshu.com/p/2defccdd14b4
 * https://developers.google.cn/ml-kit/vision/barcode-scanning
 *
 * 二维码生成 ZXING
 * https://cloud.tencent.com/developer/article/1705166
 *
 * */
@Preview(showSystemUi = true, showBackground = true)
@Composable
fun TryBarcodeScanner() {

    var isTorchEnable by remember { mutableStateOf(false) }

    var isImageAnalysisEnable by remember { mutableStateOf(true) }

    var code by remember { mutableStateOf("") }

    // 动态请求一下 CAMERA 权限 (不然连报错都没有, 直接就是黑的)
    val requestPermission =
        rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
            if (isGranted) {
                Log.i("Permission: ", "Granted")
            } else {
                Log.i("Permission: ", "Denied")
            }
        }

    LaunchedEffect(Unit) {
        requestPermission.launch(Manifest.permission.CAMERA)
    }


    if (isImageAnalysisEnable) {
        Box(
            contentAlignment = Alignment.TopCenter,
            modifier = Modifier.fillMaxSize()
        ) {

            /**
             * 相机
             */
            EasyCamera(
                isTorchEnable = isTorchEnable,
                isImageAnalysisEnable = isImageAnalysisEnable,
                onImageAnalysisSuccess = { codes ->
                    /**
                     * 扫到了就直接停止扫
                     */
                    if (codes.isNotEmpty()) {
                        code = codes.map { it.displayValue }.toString()

                        isImageAnalysisEnable = false
                    }
                }
            )

            /**
             * 裁剪框
             */
            DrawCropScan(
                topLeftScale = Offset(x = 0.2f, y = 0.25f),
                sizeScale = Size(width = 0.6f, height = 0f)
            )

            /**
             * 功能按钮组
             */
            Row(
                modifier = Modifier
                    .align(alignment = Alignment.BottomCenter)
                    .padding(bottom = 100.dp)
                    .shadow(shape = CircleShape, elevation = 1.dp, clip = true)
                    .padding(horizontal = 15.dp)
                    .background(color = cameraMainColor)
            ) {

                Button(onClick = {
                    isTorchEnable = !isTorchEnable
                }) {
                    Text(text = "torch ${if (isTorchEnable) "on" else "off"}")
                }

            }
        }

    } else {
        Column {
            Text(text = code)

            Button(onClick = { isImageAnalysisEnable = true }) {
                Text(text = "restart scan")
            }
        }
    }


}


val mainColor = Color.Gray
val cropTopLeftScale: Offset = Offset(x = 0.025f, y = 0.3f)
val cropSizeScale: Size = Size(width = 0.95f, height = 0.1f)

/**
 * camera
 */
@Composable
@Preview(showSystemUi = true, showBackground = true)
@SuppressLint("UnsafeOptInUsageError")
fun EasyCamera(
    isTorchEnable: Boolean = false,
    isImageAnalysisEnable: Boolean = false,
    onImageAnalysisStart: () -> Unit = {},
    onImageAnalysisSuccess: (List<Barcode>) -> Unit = {},
    onImageAnalysisFailure: (Exception) -> Unit = {}
) {
    val context = LocalContext.current

    val lifecycleOwner = LocalLifecycleOwner.current

    val previewView = PreviewView(context)

    val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }


    // 扫码识别器
    val imageAnalysis = ImageAnalysis.Builder()
        .setTargetResolution(android.util.Size(1280, 720))
        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
        .build()
        .apply {
            setAnalyzer(Executors.newSingleThreadExecutor()) { imageProxy ->

                if (imageProxy.image == null || !isImageAnalysisEnable) {
                    imageProxy.close()
                    return@setAnalyzer
                }

                onImageAnalysisStart()


                val mediaImage = imageProxy.image!!
                val inputImage =
                    InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)

                BarcodeScanning.getClient(
                    BarcodeScannerOptions.Builder()
                        /**
                         * 所有的条码都识别
                         */
                        .setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
                        .build()
                )
                    .process(inputImage)
                    .addOnSuccessListener(onImageAnalysisSuccess)
                    .addOnFailureListener(onImageAnalysisFailure)
                    .addOnCompleteListener {
                        imageProxy.close()
                    }
            }
        }

    // 获取相机实例
    // 注意一定要用 remember
    val camera = remember {
        cameraProviderFuture.get()
            .apply { unbindAll() }
            .bindToLifecycle(
                // 绑定 compose 的生命周期
                lifecycleOwner,

                // 使用后置摄像头
                CameraSelector
                    .Builder()
                    .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                    .build(),

                // 预览
                androidx.camera.core.Preview.Builder().build()
                    .also {
                        it.setSurfaceProvider(previewView.surfaceProvider)
                    },

                // 图像分析
                imageAnalysis,

                // 图像捕获
                ImageCapture.Builder().build()
            )
    }


    // 监听 "手电筒是否启用按钮"
    LaunchedEffect(camera, isTorchEnable) {
        if (camera.cameraInfo.hasFlashUnit()) {
            camera.cameraControl
                .enableTorch(isTorchEnable)
                .addListener({}, ContextCompat.getMainExecutor(context))
        }
    }


    /**
     * 相机本体
     */
    AndroidView(
        factory = { previewView },
        modifier = Modifier.fillMaxSize()
    )

}


/**
 * 裁剪框
 */
@Composable
fun DrawCropScan(
    topLeftScale: Offset = cropTopLeftScale,
    sizeScale: Size = cropSizeScale,
    color: Color = mainColor,
) {

    var lineBottomY by remember { mutableStateOf(0f) }

    var isAnimated by remember { mutableStateOf(true) }

    val lineYAnimation by animateFloatAsState(
        targetValue = if (isAnimated) 0f else lineBottomY,
        animationSpec = infiniteRepeatable(animation = TweenSpec(durationMillis = 1500)),
        label = ""
    )

    LaunchedEffect(true) {
        isAnimated = !isAnimated
    }

    Canvas(
        modifier = Modifier
            .fillMaxSize()
            .onGloballyPositioned {
            }
    ) {

        val paint = Paint().asFrameworkPaint()

        paint.apply {
            isAntiAlias = true
            textSize = 24.sp.toPx()
            typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
        }

        drawRect(Color.Transparent.copy(alpha = 0.1f))

        // 扫描框 高度、宽度
        var height = size.height * sizeScale.height
        var with = size.width * sizeScale.width

        // square 方形
        if (sizeScale.height == 0f) {
            height = with
        }
        if (sizeScale.width == 0f) {
            with = height
        }

        val topLeft = Offset(x = size.width * topLeftScale.x, y = size.height * topLeftScale.y)


        // 扫描框 矩形
        val rectF = Rect(offset = topLeft, size = Size(with, height))

        drawRoundRect(
            color = Color.Transparent,
            topLeft = rectF.topLeft, size = rectF.size,
            blendMode = BlendMode.Clear
        )

        // 扫描线 可到达的最大位置
        lineBottomY = height - 5.dp.toPx()


        val padding = 10.dp.toPx()

        // 扫描线
        val rectLine = Rect(
            offset = topLeft.plus(Offset(x = padding, y = lineYAnimation)),
            size = Size(with - 2 * padding, 3.dp.toPx())
        )

        // 画扫描线
        drawOval(color, rectLine.topLeft, rectLine.size)

        // 边框
        val lineWith = 3.dp.toPx()
        val lineLength = 12.dp.toPx()

        val lSizeH = Size(lineLength, lineWith)
        val lSizeV = Size(lineWith, lineLength)

        val path = Path()
        // 左上角
        path.addRect(Rect(offset = rectF.topLeft, lSizeH))
        path.addRect(Rect(offset = rectF.topLeft, lSizeV))

        // 左下角
        path.addRect(Rect(offset = rectF.bottomLeft.minus(Offset(x = 0f, y = lineWith)), lSizeH))
        path.addRect(Rect(offset = rectF.bottomLeft.minus(Offset(x = 0f, y = lineLength)), lSizeV))


        // 右上角
        path.addRect(Rect(offset = rectF.topRight.minus(Offset(x = lineLength, y = 0f)), lSizeH))
        path.addRect(Rect(offset = rectF.topRight.minus(Offset(x = lineWith, y = 0f)), lSizeV))

        // 右下角
        path.addRect(
            Rect(offset = rectF.bottomRight.minus(Offset(x = lineLength, y = lineWith)), lSizeH)
        )
        path.addRect(
            Rect(offset = rectF.bottomRight.minus(Offset(x = lineWith, y = lineLength)), lSizeV)
        )

        drawPath(path = path, color = Color.White)
    }
}
